﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace GenericGA.generationmaker
{
    /// <summary>
    /// A decorator generation maker which takes a number of the best individuals from the current generation and adds them unchanged to the next generation.
    /// </summary>
    /// <typeparam name="P">The phenotype type, or type of the candidate solutions.</typeparam>
    public class Elitism<P> : GenerationMaker<P>
    {

        /// <summary>
        /// The number of the best individuals to transfer.
        /// </summary>
        public int ElitismSize
        {
            get
            {
                return elitismSize;
            }
            set
            {
                if (value < 1)
                {
                    throw new ArgumentException();
                }
                elitismSize = value;
            }
        }
        private int elitismSize;

        /// <summary>
        /// The generation maker which generates the next generation.
        /// </summary>
        private readonly GenerationMaker<P> generationMaker;

        /// <summary>
        /// Constructor to create an elitism decorator.
        /// </summary>
        /// <param name="elitismSize">The number of the best individuals to transfer.</param>
        /// <param name="generationMaker">The generation maker which generates the next generation.</param>
        public Elitism(int elitismSize, GenerationMaker<P> generationMaker)
        {
            ElitismSize = elitismSize;
            this.generationMaker = generationMaker;
        }

        /// <summary>
        /// Return the next generation from the current generation, transferring a number of the best individuals in the current generation to the next.
        /// </summary>
        /// <param name="currGeneration">The current generation.</param>
        /// <param name="phenotypeBuildingFunction">A phenotype building function for the creation of individuals from new chromosomes.</param>
        /// <param name="fitnessFunction">A fitness function for the selection of individuals from current generation.</param>
        /// <returns>The next generation.</returns>
        public override List<List<ulong>> NextGeneration(List<Individual<P>> currGeneration, GA<P>.PhenotypeBuildingFunctionType phenotypeBuildingFunction, GA<P>.FitnessFunctionType fitnessFunction)
        {
            currGeneration.Sort(
                delegate(Individual<P> i1, Individual<P> i2)
                {
                    return i2.CachedFitness.CompareTo(i1.CachedFitness);
                }
            );
            List<List<ulong>> elite = new List<List<ulong>>();
            for (int i = 0; i < elitismSize; i++)
            {
                elite.Add(currGeneration[i].Chromosome);
            }

            List<List<ulong>> newGeneration = generationMaker.NextGeneration(currGeneration, phenotypeBuildingFunction, fitnessFunction);
            newGeneration.AddRange(elite);

            return newGeneration;
        }

    }
}
